"""
债券数据提供器

基于AkShare提供可转债相关数据的获取和处理功能。
"""

import akshare as ak
import pandas as pd
import logging
from typing import Dict, List, Optional, Any
from datetime import datetime, timedelta
import hashlib
import pickle
import os


class BondDataProvider:
    """可转债数据提供器"""
    
    def __init__(self, cache_dir: str = ".cache/akshare", cache_ttl_hours: int = 24):
        """
        初始化数据提供器
        
        Args:
            cache_dir: 缓存目录
            cache_ttl_hours: 缓存有效期（小时）
        """
        self.cache_dir = cache_dir
        self.cache_ttl_hours = cache_ttl_hours
        self.logger = logging.getLogger("tools.bond_data_provider")
        
        # 创建缓存目录
        os.makedirs(cache_dir, exist_ok=True)
    
    def _get_cache_key(self, api_name: str, params: Dict[str, Any]) -> str:
        """生成缓存键"""
        param_str = str(sorted(params.items()))
        return hashlib.md5(f"{api_name}:{param_str}".encode()).hexdigest()
    
    def _get_cache_file(self, cache_key: str) -> str:
        """获取缓存文件路径"""
        return os.path.join(self.cache_dir, f"{cache_key}.pkl")
    
    def _is_cache_valid(self, cache_file: str) -> bool:
        """检查缓存是否有效"""
        if not os.path.exists(cache_file):
            return False
        
        file_mtime = datetime.fromtimestamp(os.path.getmtime(cache_file))
        return datetime.now() - file_mtime < timedelta(hours=self.cache_ttl_hours)
    
    def _load_from_cache(self, cache_file: str) -> Optional[pd.DataFrame]:
        """从缓存加载数据"""
        try:
            with open(cache_file, 'rb') as f:
                return pickle.load(f)
        except Exception as e:
            self.logger.warning(f"加载缓存失败 {cache_file}: {e}")
            return None
    
    def _save_to_cache(self, data: pd.DataFrame, cache_file: str):
        """保存数据到缓存"""
        try:
            with open(cache_file, 'wb') as f:
                pickle.dump(data, f)
        except Exception as e:
            self.logger.warning(f"保存缓存失败 {cache_file}: {e}")
    
    async def get_bond_realtime_data(self, bond_code: Optional[str] = None) -> pd.DataFrame:
        """
        获取可转债实时行情数据
        
        Args:
            bond_code: 债券代码（可选，不指定则获取全部）
            
        Returns:
            可转债实时行情数据
        """
        cache_key = self._get_cache_key("bond_zh_cov", {"bond_code": bond_code})
        cache_file = self._get_cache_file(cache_key)
        
        # 检查缓存
        if self._is_cache_valid(cache_file):
            cached_data = self._load_from_cache(cache_file)
            if cached_data is not None:
                self.logger.info(f"从缓存加载可转债实时数据: {bond_code or '全部'}")
                return cached_data
        
        try:
            # 调用AkShare API
            df = ak.bond_zh_cov()
            
            # 数据验证
            if df.empty:
                self.logger.warning("获取的可转债数据为空")
                return pd.DataFrame()
            
            # 如果指定了债券代码，过滤数据
            if bond_code:
                df = df[df['债券代码'] == bond_code]
                if df.empty:
                    self.logger.warning(f"未找到债券代码 {bond_code} 的数据")
            
            # 保存到缓存
            self._save_to_cache(df, cache_file)
            self.logger.info(f"获取可转债实时数据成功: {len(df)} 条记录")
            
            return df
            
        except Exception as e:
            self.logger.error(f"获取可转债实时数据失败: {e}")
            return pd.DataFrame()
    
    async def get_bond_detail(self, bond_code: Optional[str] = None) -> pd.DataFrame:
        """
        获取可转债详细信息（集思录）
        
        Args:
            bond_code: 债券代码（可选）
            
        Returns:
            可转债详细信息
        """
        cache_key = self._get_cache_key("bond_cov_jsl", {"bond_code": bond_code})
        cache_file = self._get_cache_file(cache_key)
        
        if self._is_cache_valid(cache_file):
            cached_data = self._load_from_cache(cache_file)
            if cached_data is not None:
                self.logger.info(f"从缓存加载可转债详情: {bond_code or '全部'}")
                return cached_data
        
        try:
            df = ak.bond_cb_jsl()
            
            if df.empty:
                self.logger.warning("获取的可转债详情数据为空")
                return pd.DataFrame()
            
            # 重命名列以保持一致性
            column_mapping = {
                '代码': '债券代码',  # ✅ 修复：实际列名是'代码'而不是'转债代码'
                '转债名称': '债券简称',
                '正股代码': '正股代码',
                '正股名称': '正股名称'
            }
            df = df.rename(columns=column_mapping)
            
            if bond_code:
                df = df[df['债券代码'] == bond_code]
                if df.empty:
                    self.logger.warning(f"未找到债券代码 {bond_code} 的详情数据")
            
            self._save_to_cache(df, cache_file)
            self.logger.info(f"获取可转债详情数据成功: {len(df)} 条记录")
            
            return df
            
        except Exception as e:
            self.logger.error(f"获取可转债详情数据失败: {e}")
            return pd.DataFrame()
    
    async def get_bond_history(self, bond_code: str, period: str = "daily") -> pd.DataFrame:
        """
        获取可转债历史行情数据
        
        Args:
            bond_code: 债券代码
            period: 周期（daily, weekly, monthly）
            
        Returns:
            历史行情数据
        """
        cache_key = self._get_cache_key("bond_zh_cov_daily", {
            "bond_code": bond_code, 
            "period": period
        })
        cache_file = self._get_cache_file(cache_key)
        
        if self._is_cache_valid(cache_file):
            cached_data = self._load_from_cache(cache_file)
            if cached_data is not None:
                self.logger.info(f"从缓存加载历史数据: {bond_code}")
                return cached_data
        
        try:
            df = ak.bond_zh_hs_cov_daily(symbol=bond_code)
            
            if df.empty:
                self.logger.warning(f"债券 {bond_code} 的历史数据为空")
                return pd.DataFrame()
            
            # 确保日期列是datetime类型
            if '日期' in df.columns:
                df['日期'] = pd.to_datetime(df['日期'])
            
            self._save_to_cache(df, cache_file)
            self.logger.info(f"获取可转债历史数据成功: {bond_code}, {len(df)} 条记录")
            
            return df
            
        except Exception as e:
            self.logger.error(f"获取可转债历史数据失败 {bond_code}: {e}")
            return pd.DataFrame()
    
    async def get_bond_comparison_data(self, bond_code: Optional[str] = None) -> pd.DataFrame:
        """
        获取可转债比价数据
        
        Args:
            bond_code: 债券代码（可选）
            
        Returns:
            可转债比价数据
        """
        cache_key = self._get_cache_key("bond_cov_comparison", {"bond_code": bond_code})
        cache_file = self._get_cache_file(cache_key)
        
        if self._is_cache_valid(cache_file):
            cached_data = self._load_from_cache(cache_file)
            if cached_data is not None:
                self.logger.info("从缓存加载可转债比价数据")
                return cached_data
        
        try:
            df = ak.bond_cov_comparison()
            
            if df.empty:
                self.logger.warning("获取的可转债比价数据为空")
                return pd.DataFrame()
            
            # 如果指定了债券代码，过滤数据
            if bond_code:
                df = df[df['转债代码'] == bond_code]
                if df.empty:
                    self.logger.warning(f"未找到债券代码 {bond_code} 的比价数据")
            
            self._save_to_cache(df, cache_file)
            self.logger.info(f"获取可转债比价数据成功: {len(df)} 条记录")
            
            return df
            
        except Exception as e:
            self.logger.error(f"获取可转债比价数据失败: {e}")
            return pd.DataFrame()
    
    async def get_bond_redeem_info(self, bond_code: Optional[str] = None) -> pd.DataFrame:
        """
        获取可转债强赎信息（集思录）
        
        Args:
            bond_code: 债券代码（可选）
            
        Returns:
            可转债强赎信息
        """
        cache_key = self._get_cache_key("bond_cb_redeem_jsl", {"bond_code": bond_code})
        cache_file = self._get_cache_file(cache_key)
        
        if self._is_cache_valid(cache_file):
            cached_data = self._load_from_cache(cache_file)
            if cached_data is not None:
                self.logger.info("从缓存加载可转债强赎信息")
                return cached_data
        
        try:
            df = ak.bond_cb_redeem_jsl()
            
            if df.empty:
                self.logger.warning("获取的可转债强赎信息为空")
                return pd.DataFrame()
            
            # 如果指定了债券代码，过滤数据
            if bond_code:
                df = df[df['代码'] == bond_code]
                if df.empty:
                    self.logger.warning(f"未找到债券代码 {bond_code} 的强赎信息")
            
            self._save_to_cache(df, cache_file)
            self.logger.info(f"获取可转债强赎信息成功: {len(df)} 条记录")
            
            return df
            
        except Exception as e:
            self.logger.error(f"获取可转债强赎信息失败: {e}")
            return pd.DataFrame()
    
    async def get_underlying_stock_code(self, bond_code: str) -> Optional[str]:
        """
        获取可转债对应的正股代码
        
        Args:
            bond_code: 债券代码
            
        Returns:
            正股代码，如果未找到返回None
        """
        try:
            df = await self.get_bond_realtime_data(bond_code)
            if not df.empty and '正股代码' in df.columns:
                stock_code = df.iloc[0]['正股代码']
                self.logger.info(f"获取正股代码成功: {bond_code} -> {stock_code}")
                return stock_code
            return None
        except Exception as e:
            self.logger.error(f"获取正股代码失败 {bond_code}: {e}")
            return None
    
    async def validate_bond_code(self, bond_code: str) -> bool:
        """
        验证债券代码是否有效
        
        Args:
            bond_code: 债券代码
            
        Returns:
            是否有效
        """
        if not isinstance(bond_code, str) or len(bond_code) != 6:
            return False
        
        try:
            df = await self.get_bond_realtime_data(bond_code)
            return not df.empty
        except Exception:
            return False
    
    async def search_bonds_by_name(self, bond_name: str) -> List[Dict[str, Any]]:
        """
        根据债券名称搜索债券
        
        Args:
            bond_name: 债券名称（支持模糊匹配）
            
        Returns:
            匹配的债券列表
        """
        try:
            df = await self.get_bond_realtime_data()
            if df.empty:
                return []
            
            # 模糊匹配债券名称
            matched = df[df['债券简称'].str.contains(bond_name, case=False, na=False)]
            
            results = []
            for _, row in matched.iterrows():
                results.append({
                    '债券代码': row['债券代码'],
                    '债券简称': row['债券简称'],
                    '正股代码': row.get('正股代码', ''),
                    '正股名称': row.get('正股名称', ''),
                    '最新价': row.get('最新价', 0)
                })
            
            self.logger.info(f"债券名称搜索成功: '{bond_name}' -> {len(results)} 个结果")
            return results
            
        except Exception as e:
            self.logger.error(f"债券名称搜索失败 '{bond_name}': {e}")
            return []
    
    async def get_bond_basic_info(self, bond_code: str) -> Dict[str, Any]:
        """
        获取债券基本信息（整合多个数据源）
        
        Data Sources:
        1. bond_zh_cov - 主要数据源（985条数据，覆盖广）
        2. bond_cov_comparison - 比价数据（纯债价值、强赎信息）
        3. bond_cb_redeem_jsl - 强赎详细信息
        4. bond_cb_jsl - 补充数据（仅30条，可选）
        
        Args:
            bond_code: 债券代码
            
        Returns:
            债券基本信息字典
        """
        try:
            # 数据源1: bond_zh_cov (实时行情) - 主要数据源
            realtime_df = await self.get_bond_realtime_data(bond_code)
            if realtime_df.empty:
                self.logger.warning(f"未找到债券 {bond_code} 的实时数据")
                return {}
            
            realtime_row = realtime_df.iloc[0]
            
            # 基础信息（来自bond_zh_cov）
            bond_info = {
                '债券代码': bond_code,
                '债券简称': realtime_row.get('债券简称', ''),
                '正股代码': realtime_row.get('正股代码', ''),
                '正股名称': realtime_row.get('正股简称', ''),  # 注意：列名是'正股简称'
                '正股价': realtime_row.get('正股价', 0),
                '转股价': realtime_row.get('转股价', 0),
                '转股价值': realtime_row.get('转股价值', 0),
                '债现价': realtime_row.get('债现价', 0),
                '转股溢价率': realtime_row.get('转股溢价率', 0),
                '发行规模': realtime_row.get('发行规模', 0),
                '信用评级': realtime_row.get('信用评级', ''),
                '上市时间': realtime_row.get('上市时间', ''),
                '申购日期': realtime_row.get('申购日期', ''),
            }
            
            # 数据源2: bond_cov_comparison (比价数据) - 补充详细信息
            comparison_df = await self.get_bond_comparison_data(bond_code)
            if not comparison_df.empty:
                comp_row = comparison_df.iloc[0]
                bond_info.update({
                    '纯债价值': comp_row.get('纯债价值', 0),
                    '纯债溢价率': comp_row.get('纯债溢价率', 0),
                    '回售触发价': comp_row.get('回售触发价', 0),
                    '强赎触发价': comp_row.get('强赎触发价', 0),
                    '到期赎回价': comp_row.get('到期赎回价', 0),
                    '开始转股日': comp_row.get('开始转股日', ''),
                })
                self.logger.info(f"从bond_cov_comparison获取到 {bond_code} 的补充信息")
            
            # 数据源3: bond_cb_redeem_jsl (强赎信息) - 补充强赎条款
            redeem_df = await self.get_bond_redeem_info(bond_code)
            if not redeem_df.empty:
                redeem_row = redeem_df.iloc[0]
                bond_info.update({
                    '剩余规模': redeem_row.get('剩余规模', bond_info.get('发行规模', 0)),
                    '转股起始日': redeem_row.get('转股起始日', ''),
                    '到期日': redeem_row.get('到期日', ''),
                    '强赎触发比': redeem_row.get('强赎触发比', 0),
                    '强赎天计数': redeem_row.get('强赎天计数', ''),
                    '强赎条款': redeem_row.get('强赎条款', ''),
                    '强赎状态': redeem_row.get('强赎状态', ''),
                })
                self.logger.info(f"从bond_cb_redeem_jsl获取到 {bond_code} 的强赎信息")
            
            # 数据源4: bond_cb_jsl (集思录) - 可选补充（覆盖率低）
            detail_df = await self.get_bond_detail(bond_code)
            if not detail_df.empty:
                detail_row = detail_df.iloc[0]
                # 只在主数据源没有的情况下才补充
                if not bond_info.get('剩余规模'):
                    bond_info['剩余规模'] = detail_row.get('剩余规模', 0)
                if not bond_info.get('到期时间'):
                    bond_info['到期时间'] = detail_row.get('到期时间', '')
                self.logger.info(f"从bond_cb_jsl获取到 {bond_code} 的额外信息")
            
            self.logger.info(f"获取债券基本信息成功: {bond_code}, 共 {len(bond_info)} 个字段")
            return bond_info
            
        except Exception as e:
            self.logger.error(f"获取债券基本信息失败 {bond_code}: {e}")
            import traceback
            traceback.print_exc()
            return {}
    
    def clear_cache(self, older_than_hours: int = 0):
        """
        清理缓存
        
        Args:
            older_than_hours: 清理多少小时前的缓存（0表示全部清理）
        """
        try:
            cutoff_time = datetime.now() - timedelta(hours=older_than_hours)
            
            for filename in os.listdir(self.cache_dir):
                if filename.endswith('.pkl'):
                    filepath = os.path.join(self.cache_dir, filename)
                    file_mtime = datetime.fromtimestamp(os.path.getmtime(filepath))
                    
                    if older_than_hours == 0 or file_mtime < cutoff_time:
                        os.remove(filepath)
                        self.logger.info(f"清理缓存文件: {filename}")
            
            self.logger.info("缓存清理完成")
        except Exception as e:
            self.logger.error(f"缓存清理失败: {e}")


# 全局实例
bond_data_provider = BondDataProvider()